import numpy as np
import pandas as pd
import calendar
import arrow
import time
import sys
import datetime as dt
from arrow.parser import ParserError

sys.path.append("..")

from tools.project_tool import sys_exit
from tools import assert_tool
from tools import common_var


def match_format(the_date, fmt_str, throw_it=None, return_res=None):
    """
    检查给的日期是不是符合对应要求的格式
        如果匹配，返回 True；否则 False
    :param the_date: 匹配的日期
    :param fmt_str: 匹配的格式
    :param throw_it: 是不是把它抛出异常 还是 直接结束程序，默认：None
    :param return_res: 是不是将匹配结果返回，优先判定是不是返回，默认：None
    :return:
        如果匹配，返回 True
        否则：如果bool（return_res)为True，返回 False；否则，抛出异常
    """
    try:
        arrow.get(the_date, fmt_str)
    except ParserError as _:
        if fmt_str == 'YYYYMMDD':
            eg_msg = '20010101'
        elif fmt_str == 'YYYY-MM-DD':
            eg_msg = '2010-01-01'
        else:
            eg_msg = ''
        err_msg = '给的日期（%s）好像不符合给的格式（%s）' % (the_date, fmt_str)
        if eg_msg != '':
            err_msg += '，应该例如：%s' % eg_msg
        if return_res:
            return False
        else:
            sys_exit(err_msg, throw_it=throw_it)
    return True


def format_date_str(the_date, target_fmt=None):
    """
    把一个 datetime.date 或 datetime.datetime 或 str 或 int（表示日期的）转成特定的日期格式的字符串

    :param datetime.datetime or datetime.date or str or int the_date: 日期数据
    :param str target_fmt: 需要转换成的日期格式，'%Y-%m-%d'或者'%Y%m%d'
    :return: string
    """
    if target_fmt is None:
        target_fmt = common_var.PY_DATE_FMT_WITH_N_DASH

    if isinstance(the_date, dt.date):
        output_str = the_date.strftime(target_fmt)
    elif isinstance(the_date, int) or isinstance(the_date, str):
        if isinstance(the_date, int):
            the_date = str(the_date)
        if match_format(the_date, 'YYYYMMDD', return_res=True):
            source_fmt = '%Y%m%d'
        elif match_format(the_date, 'YYYY-MM-DD', return_res=True):
            source_fmt = '%Y-%m-%d'
        else:
            raise NotImplementedError('日期格式若是str，只能是20200101或者2020-01-01两种类型')
        if source_fmt == target_fmt:
            output_str = the_date
        else:
            output_str = time.strftime(target_fmt, time.strptime(the_date, source_fmt))
    else:
        raise NotImplementedError('目前日期格式支持datetime.datetime or datetime.date or str or int，请重新输入')
    return output_str


def guess_date_fmt(the_date, **kwargs):
    """
    猜测一个日期的格式

    :raise ValueError: 如果不能找到一个格式与此日期匹配
    :param str the_date: 日期
    :param kwargs:
         - arrow_like，bool，是不是 arrow 中的日期格式，默认：False
    :return: str，猜测结果
    """
    arrow_like = kwargs.get('arrow_like', False)

    fmt = None
    fmts_to_test = [common_var.PY_DATE_FMT_WITH_N_DASH, common_var.PY_DATE_FMT_WITHOUT_N_DASH, common_var.PY_TIME_FMT_WITHOUT_N_DASH, common_var.PY_TIME_FMT_WITH_N_DASH]
    for test_fmt in fmts_to_test:
        try:
            dt.datetime.strptime(the_date, test_fmt)
            fmt = test_fmt
            break
        except ValueError:
            pass
    if fmt is None:
        msg = f'日期 {the_date} 的格式不符合 {fmts_to_test} 中任意一个'
        raise ValueError(msg)
    if arrow_like:
        return common_var.DATE_FMT_1 if fmt == common_var.PY_DATE_FMT_WITH_N_DASH else common_var.DATE_FMT

    return fmt


def str_to_datetime(input_str, **kwargs):
    """
    将一个字符串转成datetime日期格式

    :param input_str: 字符串日期，最好是%Y-%m-%d %H:%M:%S 中间要有空格, 如‘2021-09-18’, '2018-01-01 00:00:00'
    :param kwargs:
        - str fmt:日期格式，默认：从配置文件读取
        - str tz_str: 时区格式， 默认为'+0800'
    :return: datetime
    """

    fmt = kwargs.pop('fmt', common_var.PY_DATE_FMT_WITH_N_DASH)  # 获取日期字符串格式
    default_tz = time.strftime('%z')  # 获取系统默认时区
    tz_str = kwargs.pop('tz_str', default_tz)

    time_ = f'{input_str} {tz_str}'
    time_fmt = f'{fmt} %z'

    if len(str(time_)) in [16, 22, 25]:  # yyyy-mm-dd格式, yyyy-mm-dd hh:mm格式, yyyy-mm-dd hh:mm:ss格式
        datetime_ = dt.datetime.strptime(time_, time_fmt)
    else:
        raise ValueError(f'日期字符串格式有误，请检查是否为[yyyy-mm-dd, yyyy-mm-dd hh:mm, yyyy-mm-dd hh:mm:ss]中的一种')

    return datetime_


def str_to_timestamp(input_str, **kwargs):
    """
    将一个字符串转成时间戳日期格式

    :param input_str: 字符串日期，最好是%Y-%m-%d %H:%M:%S 中间要有空格, 如‘2021-09-18’, '2018-01-01 00:00:00'
    :param kwargs:
        - str fmt:日期格式，默认：从配置文件读取
        - str tz_str: 时区格式， 默认为'+0800'
    :return: int timestamp
    """

    datetime_ = str_to_datetime(input_str, **kwargs)
    timestamp_ = datetime_to_timestamp(datetime_)

    return timestamp_


def str_to_date(input_str, **kwargs):
    """
    将一个字符串转成项目默认的日期格式

    :param input_str: 字符串日期，最好是%Y-%m-%d %H:%M:%S 中间要有空格, 如‘2021-09-18’, '2018-01-01 00:00:00'
    :param kwargs:
        - str fmt:日期格式，默认：从配置文件读取
        - str tz_str: 时区格式， 默认为'+0800'
    :return: date
    """
    datetime_ = str_to_datetime(input_str, **kwargs)
    date_ = datetime_.date()
    return date_


def str_to_int(the_date, source_fmt=None, target_fmt=None):
    """
    把一个str（表示日期的）转成整数, 注意这里的整数不是变成了timestamp的整数格式；而是‘2021-09-02’ 变成了 20210902

    :param str the_date: 日期数据
    :param str source_fmt: 源格式，默认：%Y-%m-%d
    :param str target_fmt: 目标格式，默认：%Y%m%d
    :return: int
    """
    if source_fmt is None:
        source_fmt = common_var.PY_DATE_FMT_WITH_N_DASH
    target_fmt = '%Y%m%d' if target_fmt is None else target_fmt
    reformated_str = time.strftime(target_fmt, time.strptime(the_date, source_fmt))
    int_date = int(reformated_str)
    return int_date


def timestamp_to_datetime(ts_epoch, **kwargs):
    """
    datestamp转datetime,pandas转出来的timestamp是13位整数 要/1000,
    It’s common for this to be restricted to years from 1970 through 2038.
    从1970年开始的纳秒到当前的计数 转变成 float 类型时间 类似 time.time() 返回的类型

    :param ts_epoch:
    :param kwargs:
        - str tz_str: 时区格式， 默认为'+0800'
    :return:
    """

    tz_str = kwargs.pop('tz_str', '')
    if tz_str == '':  # 获取系统默认时区
        default_tz = time.strftime('%z')
        timezone_offest = int(default_tz[2])
    else:  # 根据指定时区字符串，得到时区偏移量
        timezone_offest = int(tz_str[1:3])
    tz_ = dt.timezone(dt.timedelta(hours=timezone_offest))

    if (ts_epoch is None):
        ts_epoch = dt.datetime.now(tz=tz_)

    if isinstance(ts_epoch, int) or isinstance(ts_epoch, np.int32) or isinstance(ts_epoch, np.int64) or isinstance(ts_epoch, float):
        # 方法1
        datetime_ = dt.datetime.fromtimestamp(ts_epoch, tz=tz_)
        # 方法2
        # datetime_ = (datetime(1970, 1, 1, tzinfo=timezone.utc) + timedelta(seconds=int(ts_epoch))).astimezone(tz_)
    else:
        raise Exception('No support type %s.' % type(ts_epoch))

    return datetime_


def timestamp_to_str(ts_epoch=None, **kwargs):
    """

    :param ts_epoch: 单位为秒
    :param kwargs:
        - str fmt:日期格式，默认：从配置文件读取
        - str tz_str: 时区格式， 默认为'+0800'
    :return:
    """
    fmt = kwargs.pop('fmt', common_var.PY_DATE_FMT_WITH_N_DASH)  # 获取日期字符串格式
    datetime_ = timestamp_to_datetime(ts_epoch, **kwargs)
    str_date = datetime_.strftime(fmt)

    return str_date


def datetime_to_timestamp(date_=None):
    """
    返回当前UTC时间戳，输入的date_如果没有指定时区默认时区为北京时间

    :return: 类型 int
    """

    if (date_ is None):
        tz = dt.timezone(dt.timedelta(hours=8))
        date_ = dt.datetime.now(tz)

    # 方法1
    # original_datetime = datetime(1970, 1, 1, tzinfo=timezone.utc)
    # time_stamp = int((date_ - original_datetime).total_seconds())

    # 方法2
    time_stamp = int(dt.datetime.timestamp(date_))

    # 方法3
    # time_stamp = int(time.mktime(date_.timetuple()))  # 这种方式会丢掉时区信息, 默认为系统时区，不建议采用

    return time_stamp


def timezone_change(time_, src_timezone=None, target_timezone=None, target_format=None):
    """
    将任一时区的时间转换成指定时区的时间； 如果没有指定目的时区，则默认转换成当地时区

    :param str or datetime time_: 日期格式
    :param str or int src_timezone: 要转换的源时区，默认为系统时区，输入格式如"Asia/Shanghai"， "UTC", 或者为5， 8，时区的偏移量
    :param str or int target_timezone: 要转换的目的时区，如"Asia/Shanghai", 如果没有指定目的时区，则默认转换成当地时区
    :param str target_format: 时间格式
    :return: datetime or str, 字符串时间格式, 或者
    """

    if not target_format:
        target_format = "%Y-%m-%d %H:%M:%S"

    # 将时间格式统一为datetime形式
    old_tz_ = get_timezone_offset(src_timezone)
    if isinstance(time_, str):
        date_fmt = guess_date_fmt(time_)
        old_datetime_ = str_to_datetime(time_, fmt=date_fmt, tz_str=old_tz_)
    else:
        old_datetime_ = time_

    # 新的时区信息
    new_tz_ = get_timezone(target_timezone)
    new_datetime_ = old_datetime_.astimezone(new_tz_)  # 时区转换

    new_datetime_ = new_datetime_.strftime(target_format)

    return new_datetime_


# 按照某一规则得到某个或某些日期或时间
def get_datetime_and_timestamp():
    """
    获得当前datetime和timestamp
    MySQL数据库中的表都有titime、tutime、tutimestamp三列

    :return: datetime, datetime, timestamp
    """
    i_t = dt.datetime.now()  # 插入时间
    u_t = dt.datetime.now()  # 更新时间
    u_t_ts = time.time() * 10 ** 6  # 更新时间戳，2019年12月18日10:34:56
    # u_t_ts = time.time()  # 更新时间戳
    return i_t, u_t, u_t_ts


def add_timestamp(df):
    """
    给一个DataFrame加上三列时间，方便写入数据库

    :param pd.DataFrame df: 需要加三列的DataFrame
    :return: pd.DataFrame
    """
    df['titime'] = dt.datetime.now()
    df['tutime'] = dt.datetime.now()
    df['tutimestamp'] = time.time() * 1e6
    return df


def get_specific_weekday_in_range(start_date, end_date, weekday='Monday'):
    """
    获取start_date 到 end_date 之间的所有星期几

    :param str start_date: 如 "2022-01-01"，或者"20220101"
    :param str end_date:  如 "2022-03-01"，或者"20220101"
    :param str or int weekday: 如 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' 或者 1,2,3,4,5,6,7
    :return:
    """

    s = pd.date_range(start_date, end_date, freq='D').to_series()
    if isinstance(weekday, int):
        # weekday = s.dt.dayofweek
        weekday_series = s.dt.weekday + 1
    else:
        weekday_series = s.dt.day_name()

    date_series = weekday_series[weekday_series == weekday]
    date_list = []
    for i in date_series.index:
        date_list.append(i.strftime('%Y-%m-%d'))

    return date_list


def get_specific_weekday_this_month(the_date, weekday='Monday'):
    """
    获取the_date日期对应的这个月的所有周几， 返回的格式根据the_date的格式确定

    :param str the_date: 格式，'202007', 或者‘2020-07’, 或者是完整的日期'20200730', 或者‘2020-07-30’
    :param str or int weekday: 如 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' 或者 1,2,3,4,5,6,7
    :return:
    """

    if len(the_date) == 7:  # 即输入的格式为‘2020-07’
        start_date = the_date + '-01'
        end_date = get_the_end_of_this_month(the_date)
    elif len(the_date) == 6:  # 即输入的格式为‘202007’
        start_date = the_date + '01'
        end_date = get_the_end_of_this_month(the_date)
    elif len(the_date) == 10 or len(the_date) == 8:  # 即输入的格式为‘2020-07-30’或者'20200730'
        start_date = the_date[:-2] + '01'
        end_date = the_date
    else:
        raise ValueError('输入的数据格式不正确')

    date_list = get_specific_weekday_in_range(start_date, end_date, weekday)
    return date_list


def get_specific_weekday_of_each_quarter_last_month(start_date, end_date, weekday='Monday', num=3, ascending=True):
    """
    获取每个季度末的那个月的第几个周几，或者倒数第几个周几的日期

    :param start_date:
    :param end_date:
    :param str or int weekday: 如 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'
                               或者 1,2,3,4,5,6,7
    :param num: 第几个
    :param bool ascending: 默认为True, 为正数， False 为倒数
    :return:
    """

    quarter_ts = pd.date_range(start_date, end_date, freq='Q').to_series()
    quarter_date = [x.strftime("%Y-%m-%d") for x in quarter_ts]

    specific_weekday_list = []
    for i in quarter_date:
        date_list = get_specific_weekday_this_month(i, weekday)
        if not ascending:
            date_list.reverse()
        specific_weekday = date_list[num - 1]
        specific_weekday_list.append(specific_weekday)
    return specific_weekday_list


def get_hour_minute():
    """
    获取当前的时分

    :return: str 16:06
    """
    return get_hour_minute_second()[:5]


def get_hour_minute_second():
    """
    获取当前的时分秒

    :return: str 16:06:55
    """
    right_now = dt.datetime.now()
    right_now = format_date_str(right_now, target_fmt=common_var.PY_TIME_FMT_WITH_N_DASH)
    right_now = right_now[-8:]
    return right_now


def get_date_hour_minute():
    """
    获取当前的日期和时分
    :return: str 2020-02-02 16:06
    """
    return format_date_str(the_date=dt.datetime.now(), target_fmt=common_var.PY_TIME_FMT_WITH_N_DASH_HOUR_MIN)


def get_date_hour_minute_second():
    """
    得到当前时间 '2020-12-19 16:14:51'

    :return: str
    """
    return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))


def get_timezone_offset(tz_info=None, str_format=True):
    """
    获取指定时间相对于UTC的时区offset

    :param int or str tz_info: 默认为系统时区，输入格式如"Asia/Shanghai"， "UTC", 或者为5， 8，时区的偏移量
    :param bool str_format: 返回是int格式还是str格式

    :return: int or str: 返回int 则表示偏移的秒数， str 则返回'+0800'这种格式
    """

    def _format_offset(seconds_offset):
        """
        将偏移秒数转换为U±X
        注意：这里没有考虑时区偏移非整小时的，使用请修改处理方式

        :param seconds_offset 偏移秒数
        :return: 格式化后的时区偏移
        """

        datetime = str(dt.timedelta(seconds=abs(seconds_offset)))
        hours_minutes_seconds = datetime.split(':')

        if seconds_offset >= 0:
            return "+" + str(hours_minutes_seconds[0]).zfill(2) + str(hours_minutes_seconds[1]).zfill(2)
        else:
            return "-" + str(hours_minutes_seconds[0]).zfill(2) + str(hours_minutes_seconds[1]).zfill(2)

    if tz_info is None:
        # # 方法一：
        # tz_str = time.strftime('%z')
        # hours_offset = int(tz_str[1:3])
        #
        # # 方法二：
        # seconds_offset = time.localtime().tm_gmtoff

        # 方法三：
        is_dst = time.daylight and time.localtime().tm_isdst > 0
        seconds_offset = - (time.altzone if is_dst else time.timezone)

        # # 方法四：
        # ts = time.time()
        # seconds_offset = (datetime.fromtimestamp(ts) - datetime.utcfromtimestamp(ts)).total_seconds()

        # # 方法五：
        # import tzlocal
        # seconds_offset = int(datetime.now(tzlocal.get_localzone()).utcoffset().total_seconds())

        # # 方法六
        # tz_string = datetime.now(timezone.utc).astimezone().tzname()

    else:
        tz_ = get_timezone(tz_info)
        seconds_offset = int(dt.datetime.now(tz=tz_).utcoffset().total_seconds())

    if str_format:
        offset = _format_offset(seconds_offset)
    else:
        offset = seconds_offset

    return offset


def get_timezone(tz_info=None):
    """
    生成特定时区信息

    :param int str tz_info: int 时区偏移量, 如8代表北京时间
                            str '区域/城市' 美东时间用 'America/New_York' 来设定 北京时间用 'Asia/Shanghai' 来设定
    :return:
    """

    if isinstance(tz_info, int):  # 输入的是偏移量，单位为小时
        if tz_info is not None:
            tz_ = dt.timezone(dt.timedelta(hours=tz_info))
            # datetime(2020, 6, 27, 21, 30, tzinfo=tz_)
    elif isinstance(tz_info, str):  # 输入的是区域/城市
        if tz_info.upper() == 'UTC':
            tz_ = dt.timezone.utc
            # import pytz
            # tz_ = pytz.utc
            # from dateutil import tz
            # tz_ = tz.UTC
        else:
            # 方法1
            from dateutil import tz
            tz_ = tz.gettz(tz_info)
            if tz_ is None:
                from dateutil.zoneinfo import getzoneinfofile_stream, ZoneInfoFile
                list = ZoneInfoFile(getzoneinfofile_stream()).zones.keys()
                raise ValueError(f'时区{tz_info}不支持，请检查支持的时区{list}')

            # 方法2
            # import pytz
            # tz_ = pytz.timezone(tz_info)
            # list = pytz.all_timezones
    else:  # 获取当地时区
        offset = get_timezone_offset(str_format=False)
        tz_ = dt.timezone(dt.timedelta(seconds=offset))

    return tz_


def get_today(**kwargs):
    """
    获取今天日期，可以直接指定fmt, 也可以通过marker指定格式

    :param kwargs:
        - str marker: 'without_n_dash'-不带杠字符串, 'with_n_dash'-带杠字符串
        - str fmt: 日期格式，'%Y%m%d' 或 '%Y-%m-%d'
        - str tz_str: 时区格式， 默认为'+0800'
    :return: str，如：2020-02-26
    """
    fmt = kwargs.get('fmt', None)
    if fmt is None:
        marker = kwargs.get('marker', 'without_n_dash')
        assert_tool.value_in_list(marker, ['without_n_dash', 'with_n_dash'])
        if marker == 'without_n_dash':
            fmt = common_var.PY_DATE_FMT_WITHOUT_N_DASH
        else:
            fmt = common_var.PY_DATE_FMT_WITH_N_DASH

    tz_str = kwargs.pop('tz_str', '+0800')  # :param str or int src_timezone: 要转换的源时区，默认为系统时区，输入格式如"Asia/Shanghai"， "UTC", 或者为5， 8，时区的偏移量
    if tz_str == '+0800':
        today_str = time.strftime(fmt, time.localtime(time.time()))
    else:
        now_ = time.time()
        now_ = timestamp_to_datetime(now_, tz_str=tz_str)
        # today_str = timezone_change(today_.strftime(fmt), src_timezone="Asia/Shanghai", target_timezone=0, target_format=fmt)
        today_str = now_.strftime(fmt)

    return today_str


def get_yesterday(**kwargs):
    """
    获得昨天的日期

    :param kwargs:
        - str marker: 'without_n_dash'-不带杠字符串, 'with_n_dash'-带杠字符串
        - fmt: 日期格式，'%Y%m%d' 或 '%Y-%m-%d'

    :return: str，如：2020-02-25
    """
    today = get_today(**kwargs)
    yesterday = date_shifter(before=today, step='days', how_many=-1)
    return yesterday


def get_quarter_start(the_date):
    """
    获取日期所在季度的第一天

    :param the_date:
    :return:
    """
    date = arrow.get(the_date, "YYYY-MM-DD")
    return date.floor("quarter").format("YYYY-MM-DD")


def get_quarter_end(the_date):
    """
    获取日期所在季度的最后一天

    :param the_date:
    :return:
    """
    date = arrow.get(the_date, "YYYY-MM-DD")
    return date.ceil("quarter").format("YYYY-MM-DD")


def get_last_quarter_end(the_date):
    """
    获取日期所在季度的上个季度的最后一天,  返回的格式根据the_date的格式确定

    :param str the_date: 格式，'202007', 或者‘2020-07’, 或者是完整的日期'20200730', 或者‘2020-07-30’
    :return:
    """

    if len(the_date) == 7:  # 即输入的格式为‘2020-07’
        the_date = the_date + '-01'
    elif len(the_date) == 6:  # 即输入的格式为‘202007’
        the_date = the_date + '01'
    elif len(the_date) == 10 or len(the_date) == 8:  # 即输入的格式为‘2020-07-30’或者'20200730'
        pass
    else:
        raise ValueError('输入的数据格式不正确')

    last_quarter_end = date_shifter(get_quarter_end(the_date), step='quarters', how_many=-1)

    return last_quarter_end


def get_the_end_of_last_month(the_date):
    """
    获取the_date日期对应的上个月最后一天的日期， 返回的格式根据the_date的格式确定

    :param str the_date: 格式，'202007', 或者‘2020-07’, 或者是完整的日期'20200730', 或者‘2020-07-30’
    :return:
    """

    if len(the_date) == 7:  # 即输入的格式为‘2020-07’
        first_day_of_this_month = the_date + '-01'
    elif len(the_date) == 6:  # 即输入的格式为‘202007’
        first_day_of_this_month = the_date + '01'
    elif len(the_date) == 10 or len(the_date) == 8:  # 即输入的格式为‘2020-07-30’或者'20200730'
        first_day_of_this_month = the_date[:-2] + '01'
    else:
        raise ValueError('输入的数据格式不正确')

    last_day = date_shifter(before=first_day_of_this_month, step='days', how_many=-1)
    return last_day


def get_the_end_of_this_month(the_date):
    """
    获取the_date日期对应的该月最后一天的日期， 返回的格式根据the_date的格式确定

    :param str the_date: 格式，'202007', 或者‘2020-07’, 或者是完整的日期'20200730', 或者‘2020-07-30’
    :return:
    """
    if len(the_date) == 7:  # 即输入的格式为‘2020-07’
        the_date = the_date + '-01'
    elif len(the_date) == 6:  # 即输入的格式为‘202007’
        the_date = the_date + '01'

    # 获取当前月总天数
    date_fmt_str = guess_date_fmt(the_date=the_date, arrow_like=False)
    dt_date = dt.datetime.strptime(the_date, date_fmt_str)
    year = dt_date.year
    month = dt_date.month
    weekday, monthcountday = calendar.monthrange(year, month)
    # monthcountday = 28 if my_month == 2 else monthcountday
    # 获取当前月份最后一天
    lastday = dt.date(year, month, day=monthcountday)
    lastday = lastday.strftime(date_fmt_str)

    return lastday


def get_month_of_a_date(**kwargs):
    """
    通过一个给定的日期，计算这个日期的月份，如果没给，计算当前月份

    :param kwargs:
        - the_date，给定的日期，默认当前日期
        - fmt，该日期的格式，如果没有指定，通过try去把这个格式给试出来，然后使用（如果知道的话，尽量还是指定下）
    :return:
        如果传入的日期→2018-01-01，返回→2018-01
        如果传入的日期→20180101，返回→201801
    """
    the_date = kwargs.get('the_date', get_today())
    fmt = kwargs.get('fmt')
    if fmt is None:
        fmt = guess_date_fmt(the_date=the_date)
    if fmt == common_var.PY_DATE_FMT_WITH_N_DASH:
        return the_date[:7]
    else:
        return the_date[:6]


def time_plus_one_minute(hour_minute):
    """
    将一个时间往后移一分钟

    :param str hour_minute: 时间，格式"15:00"
    :return: str
    """
    hour, minute = hour_minute.split(':')
    new_minute = int(minute) + 1
    new_hour = int(hour)
    if new_minute > 60 or new_hour > 24:
        raise ValueError(f'傳入的數據不能執行分鐘加一：{hour_minute}')
    if new_minute == 60:
        new_hour += 1
        new_hour = '00' if new_hour == 24 else str(new_hour)
        new_minute = '00'
    else:
        new_minute = str(new_minute)
        new_hour = str(new_hour)
    if len(new_hour) == 1:
        new_hour = '0' + new_hour
    if len(new_minute) == 1:
        new_minute = '0' + new_minute
    ans = new_hour + ':' + new_minute

    # print(f'輸入={x}，輸出={ans}')
    return ans


# 时间，日期相关的操作
def date_shifter(before, step, how_many, fmt_str=None):
    """
    计算一个给定日期的前或后多少天，周，月，季，年的日期是多少

    :raise ValueError: 如果给的步数 how_many 不是 int
    :raise NotImplementedError: 如果给的步长 step 不是已经考虑的
    :param str before: 给定的日期, 带横杆或者不带横杆
    :param str step: 步长：seconds-秒；minutes-分钟；hours-小时；days-天；
                          weeks-周；months-月；quarters-季；years-年
    :param int how_many: 调多少步
    :param str fmt_str: 时间的格式，默认：None（会自动去匹配输入的格式）
    :return: str，调整之后的日期
    """
    # if not isinstance(how_many, int):
    #     raise ValueError('相差的个数必须是int，现在是：%s' % how_many.__class__)

    if fmt_str is None:
        fmt_str = guess_date_fmt(the_date=before, arrow_like=True)

    before_arrow = arrow.get(before, fmt_str)
    if step == 'seconds':
        after = before_arrow.shift(seconds=how_many)
    elif step == 'minutes':
        after = before_arrow.shift(minutes=how_many)
    elif step == 'hours':
        after = before_arrow.shift(hours=how_many)
    elif step == 'days':
        after = before_arrow.shift(days=how_many)
    elif step == 'weeks':
        after = before_arrow.shift(weeks=how_many)
    elif step == 'months' or step == 'quarters' or step == 'years':
        if step == 'quarters':
            how_many = how_many * 3
        if step == 'years':
            how_many = how_many * 12
        # 判断当前日期是否为该月最后一天
        the_end_of_this_month = get_the_end_of_this_month(before)
        if before == the_end_of_this_month:  # 如果这天正好是这个月最后一天， 则要求区间起始日期也为那个月最后一天
            after = before_arrow.shift(months=how_many)  # 起始日期arrow格式
            after_str = after.format(fmt_str)  # 起始日期字符串格式
            the_end_of_after_month = get_the_end_of_this_month(after_str)  # 获取起始月的最后一天
            if after_str != the_end_of_after_month:  # 如果起始日期不为起始月的最后一天
                the_end_of_after_month = arrow.get(the_end_of_after_month, fmt_str)
                after = the_end_of_after_month
        else:  # 如果这天不是这个月最后一天， 则正常操作
            after = before_arrow.shift(months=how_many)
    else:
        raise NotImplementedError(f'还没实现 {step} (已有：seconds, minutes, hours, days, weeks, months, quarters, years)')
    after = after.format(fmt_str)
    return after


def find_interval_start(end_date, interval_name, interval_type='nav', freq='D'):
    """
    寻找一个日期在某一时间区间之前的另一个日期

    :param str end_date: 需要寻找的日期'20200830'或‘2020-08-30’，输出的日期格式会自动匹配end_date的日期格式
    :param str interval_name: 时间区间，取值1w、1m、3m、6m、1y、2y、3y、5y、10y、ytd、incep
    :param str interval_type: 区间类型，ret为左开右闭，nav为左闭右闭，如果freq='D'，start往后移一天，如果freq='M'，start往后移一月
    :param str freq: 返回日期精确到月还是天, 支持'D', 'M'
    :return:
    """

    assert freq in ['D', 'M']
    start = how_many = step = None
    # 如果freq='M'而且end_date不是月末最后一天，将end_date调整为上个月末； 如end_date=2020-10-12， 则将end_date调整为2020-09-30
    if end_date != get_the_end_of_this_month(the_date=end_date) and freq == 'M':
        end_date = get_the_end_of_last_month(end_date)

    if interval_name == '1w':
        step, how_many, msg = 'weeks', 1, '周'
        if freq == 'M':
            # LOG.info(f'区间为周，但是展示的日期为月，建议freq设置为‘D’')
            pass
    elif interval_name == '1m':
        step, how_many, msg = 'months', 1, '月'
    elif interval_name == '3m':
        step, how_many, msg = 'months', 3, '月'
    elif interval_name == '6m':
        step, how_many, msg = 'months', 6, '月'
    elif interval_name == '1y':
        step, how_many, msg = 'years', 1, '年'
    elif interval_name == '2y':
        step, how_many, msg = 'years', 2, '年'
    elif interval_name == '3y':
        step, how_many, msg = 'years', 3, '年'
    elif interval_name == '5y':
        step, how_many, msg = 'years', 5, '年'
    elif interval_name == '10y':
        step, how_many, msg = 'years', 10, '年'
    elif interval_name == 'ytd':
        step, how_many, msg = 'years', 1, '今年以来'
    elif interval_name == 'incep':
        msg = '成立以来'
        start = None
    else:
        raise NotImplementedError('还没实现你要的interval')
    if step:  # step不为None, 即时间区间不是成立以来
        start = date_shifter(before=end_date, step=step, how_many=-how_many)
        if interval_name == 'ytd':  # 如果为今年以来，则起始时间为去年年末
            if len(end_date) == 8:
                start = start[:4] + '1231'
            elif len(end_date) == 10:
                start = start[:4] + '-12-31'
        if interval_type == 'ret' and freq == 'D':
            start = date_shifter(before=start, step='days', how_many=1)  # 如果是ret且D则start往后移一天
        if interval_type == 'ret' and freq == 'M':
            start = date_shifter(before=start, step='months', how_many=1)  # 如果是ret且M则start往后移一个月
    if freq == 'M':
        if len(end_date) == 8:
            start = start[:6] if start is not None else None
            end = end_date[:6]
        elif len(end_date) == 10:
            start = start[:7] if start is not None else None
            end = end_date[:7]
        else:
            # LOG.error(f"输入的end_date格式为2020-09-10或20200910，请重新输入")
            return
    elif freq == 'D':
        end = end_date
    else:
        # LOG.error(f"freq只支持D和M，请重新输入")
        return
    # LOG.info(f'判断时点为{end_date}, 区间类型为{interval_type}, {freq}频，{end} 的 {how_many} {msg} 前 → {start}')
    return start, end


def get_monthly_sub_intervals(start_date, end_date, fmt='YYYY-MM-DD', **kwargs):
    """
    获取一个日期区间的子区间
    返回开始时间到结束时间之间， 每月第一天或最后一天
    如，指定2020-01-01到2020-03-15，
    返回[('2020-01-01', '2020-01-31'), ('2020-01-31', '2020-02-29'), ('2020-02-29', '2020-03-15')]

    :param str start_date: 开始日期，格式'2020-01-01'或'20200101'
    :param str end_date: 结束日期，格式'2020-01-01'或'20200101'
    :param str fmt: 日期格式，默认 YYYY-MM-DD
    :param kwargs:
        - head_or_tail，str，头还是尾，默认 头（head）-月初第一天；尾（tail）-月末最后一天
    :return: list
    """
    head_or_tail = kwargs.pop('head_or_tail', 'head')
    # LOG.info(f'{start_date}→{end_date}，'f'head_or_tail={head_or_tail}，粒度：months，格式：{fmt}')

    match_format(start_date, fmt)
    match_format(end_date, fmt)
    if start_date > end_date:
        # LOG.warning('开始（%s）比结束（%s）大', start_date, end_date)
        return []
    tmp_start = start_date[:-2] + '01'
    if head_or_tail == 'head':
        tmp_start = date_shifter(before=tmp_start, step='months', how_many=1, fmt_str=fmt)
    res = []
    next_start = tmp_start
    while next_start <= end_date:
        next_end = date_shifter(next_start, 'months', 1, fmt_str=fmt)
        if next_end <= end_date:
            tmp = [next_start, next_end]
            res.append(tmp)
        next_start = next_end
    for x in res:
        x[0] = date_shifter(before=x[0], step='days', how_many=-1, fmt_str=fmt)
        x[1] = date_shifter(before=x[1], step='days', how_many=-1, fmt_str=fmt)
    if len(res) == 0:
        res.append((start_date, end_date))
    else:
        if head_or_tail == 'head':
            if res[0][0] > start_date:
                res.insert(0, [start_date, res[0][0]])
        else:
            res[0][0] = max(start_date, res[0][0])
    if res[-1][-1] < end_date:
        res.append([res[-1][-1], end_date])
    res = [tuple(x) for x in res]

    return res


def check_start_and_end(start_date, end_date, fmt=None):
    """
    检查开始和结束时间的格式及大小关系

    :param str start_date: 开始日期
    :param str end_date: 结束日期
    :param str fmt: 日期格式
    :return: bool
    """
    fmt = 'YYYY-MM-DD' if fmt is None else fmt
    # 首先是格式检查
    if start_date is not None:
        match_format(start_date, fmt)
    if end_date is not None:
        match_format(end_date, fmt)

    # 然后开始不应该大于结束
    if start_date is not None and end_date is not None:
        if start_date > end_date:
            raise ValueError('开始 %s 不应该大于结束 %s' % (start_date, end_date))


def run_from_start_to_end(func, start_date, end_date=None, **kwargs):
    """
    运行一个函数，这个函数有 today（可自定义） 作为关键字参数
        - 给定日期区间 [A，B]，从大到小循环，即从日期 B 运行 到日期 A
        - 可指定 间隔
        - 可指定 是否忽略错误
    :raise NotImplementedError: 传入的 step 不是 daily 或 monthly 其中一个
    :param func func: 需要被运行的函数
    :param str start_date: 区间开始
    :param str end_date: 区间结束，默认：None
    :param kwargs:
        - skip_err，bool，是否跳过错误，默认：False
        - date_idx，str，需要作为日期的关键字的名字，默认：today
        - step，str，区间的粒度，默认：daily
        - kwds_4_func，dict，即将传给待运行函数的关键字参数，默认：{}
        - args_4_func，tuple，即将传给待运行函数的位置参数，默认：()
    :return:
    """
    skip_err = kwargs.pop('skip_err', False)
    date_idx = kwargs.pop('date_idx', 'today')
    step = kwargs.pop('step', 'daily')
    kwds_4_func = kwargs.pop('kwds_4_func', {})
    args_4_func = kwargs.pop('args_4_func', ())

    date_from = end_date
    if date_from is None:
        date_from = get_yesterday()
    if step == 'daily':
        n_step = 'days'
    elif step == 'monthly':
        n_step = 'months'
    else:
        raise NotImplementedError(f'daily或monthly中一个，现在是：{step}')
    # LOG.warning(f'倒序，从 {date_from} 到 {start_date}，间隔={step}。'f'跳过错误？={skip_err}。关键字中日期索引={date_idx}')
    date_now = date_from
    while date_now >= start_date:
        date_next = date_shifter(before=date_now, how_many=-1, step=n_step)
        kwds_4_func.update({date_idx: date_now})
        # LOG.warning(f'现在运行：{date_idx}={date_now}，下一个：{date_next}')
        try:
            func(*args_4_func, **kwds_4_func)
        except Exception as err:
            # LOG.error(f'处理日期为 {date_now} 时，发生错误：{err}')
            if not skip_err:
                raise err
        # LOG.warning(f'运行成功：{date_idx}={date_now}，接下来：{date_next}')
        date_now = date_next


def calc_date_diff(start_date, end_date, step='days', fmt_str='%Y-%m-%d'):
    """
    计算两个日期相差:天(或年，月)数，和两个日期的变量名

    :param str start_date: 开始日期，格式'2020-01-01'或者'20200101'
    :param str end_date: 结束日期，格式'2020-01-01'或者'20200101'
    :param str step: days-天，weeks-周，months-月
    :param fmt_str: 时间字符串的格式，'%Y%m%d'或者'%Y-%m-%d'
    :return: int，表示相差的值
    """
    start_date = time.strptime(start_date, fmt_str)
    end_date = time.strptime(end_date, fmt_str)

    # 根据上面需要计算日期还是日期时间，来确定需要几个数组段。
    # 下标0表示年，小标1表示月，依次类推...
    start_date = dt.datetime(start_date[0], start_date[1], start_date[2])
    end_date = dt.datetime(end_date[0], end_date[1], end_date[2])

    if step == 'days':
        return (end_date - start_date).days
    elif step == 'months':
        return round((end_date - start_date).days / 30, 6)
    else:
        raise NotImplementedError(f'不能去寻找你想要的类型：{step}')


def get_date_list(start_date, end_date, **kwargs):
    """
    获得月份之间的日期列表
        输入2018-04和2018-06，输出：2018-04-01,2018-05-01,2018-06-01,2018-07-01
        相当于4，5，6月份的开始和7月的开始，这样就可计算4,5,6三个月的每月收益了，需要四个点的数据
    :param start_date: 开始，例如：2018-04
    :param end_date: 结束，例如：2018-06
    :param kwargs:
        - date_fmt，str，日期格式，默认：来自配置文件
        - front_or_end，str，需要 每个月的 第一天front 还是 最后一天end，默认：front
    :return:
        - list，根据 front_or_end 不同，返回值不同
        - front_or_end = front：['2018-04-01', '2018-05-01', '2018-06-01']
        - front_or_end = end：  ['20180431', '20180531', '20180630']
    """
    date_fmt = kwargs.pop('date_fmt', common_var.DATE_FMT_1)
    front_or_end = kwargs.pop('front_or_end', 'front')
    # LOG.info(f'从 {start_date} 到 {end_date}，格式：{date_fmt}，要开始还是结束：{front_or_end}')

    assert_tool.value_in_list(date_fmt, [common_var.DATE_FMT, common_var.DATE_FMT_1], name='日期格式')
    assert_tool.value_in_list(front_or_end, ['front', 'end'], name='front_or_end')

    if start_date > end_date:
        # LOG.warning('开始日期大于结束日期，直接返回 None')
        return

    tail = '-01' if date_fmt == common_var.DATE_FMT_1 else '01'
    start_date += tail
    end_date += tail
    a_tmp = arrow.get(end_date, date_fmt)
    end_date = a_tmp.shift(months=1).format(date_fmt)
    tmp_date = start_date
    ans_list = []
    while tmp_date <= end_date:
        ans_list.append(tmp_date)
        data_arrow = arrow.get(tmp_date, date_fmt)
        tmp_date = data_arrow.shift(months=1).format(date_fmt)

    if front_or_end == 'end':
        ans_list = list(map(lambda x: date_shifter(x, 'days', -1, date_fmt), ans_list))
    # print(ans_list)
    # assert False
    return ans_list


# 学习测试
def creat_date():
    date_ = dt.date(2020, 10, 12)  # date 是一个理想化的简单型日期，属性有year , month , day
    date_ = dt.date.today()
    date_.year, date_.month, date_.day  # date 类型的日期，可以通过.year , .month , .day 来获取日期所属的年份，月份，和具体的日期号

    return date_


def creat_time():
    time_ = dt.time(20, 10, 1)  # time 是一个独立于任何特定日期的理想化时间，其属性有hour，minute，second ，microsecond 和tzinfo
    time_.hour, time_.minute, time_.second, time_.microsecond, time_.tzinfo

    return time_


def creat_datetime():
    datetime_ = dt.datetime(2020, 10, 11, 20, 10, 1)  # datetime 是日期和时间的结合，其属性有year，month，day，hour，minute，second ，microsecond 和tzinfo
    datetime_ = dt.datetime(2020, 10, 14, 15, 12, 20, 303269)
    datetime_ = dt.datetime.now()
    datetime_ = dt.datetime.today()
    date_ = dt.date(2020, 10, 12)
    time_ = dt.time(20, 10, 1)
    datetime_ = dt.datetime.combine(date_, time_)
    datetime_.date(), datetime_.time()
    datetime_.year, datetime_.month, datetime_.day  # date 类型的日期，可以通过.year , .month , .day 来获取日期所属的年份，月份，和具体的日期号
    datetime_.weekday()  # 使用weekday 方法 数字从0 开始， 0 代表星期一， 1 代表星期二， 以此类推
    datetime_.isoweekday()  # 使用isoweekday 方法 数字从1 开始， 1 代表星期一， 2 代表星期二， 以此类推

    """
    datetime 还有一种方法，在这里也值得介绍下，如果datetime 的值由于某些原因弄错了，我们也可以通过replace() 方法来进行更正。这个方法在进行数据清洗的时候会有用。
    replace 方法的参数如下：
    datetime.replace(year=self.year, month=self.month, day=self.day, hour=self.hour, 
    minute=self.minute, second=self.second, microsecond=self.microsecond, tzinfo=self.tzinfo, * fold=0)
    """
    datetime_ = datetime_.replace(year=2019)
    import pytz
    sh = pytz.timezone('Asia/Shanghai')
    datetime_ = datetime_.replace(tzinfo=sh)

    return datetime_


def create_timestamp():
    t1 = time.time()  # time.time() 返回当前时间的时间戳 float, 单位是秒
    # 返回当前时间的时间元组（ struct_time ）
    t2 = time.localtime()  # time.struct_time(tm_year=2020, tm_mon=10, tm_mday=15, tm_hour=10, tm_min=26, tm_sec=38, tm_wday=3, tm_yday=289, tm_isdst=0)
    # 返回当前时间的UTC 时区的时间元组（ struct_time ）
    t3 = time.gmtime()  # time.struct_time(tm_year=2020, tm_mon=10, tm_mday=15, tm_hour=2, tm_min=26, tm_sec=49, tm_wday=3, tm_yday=289, tm_isdst=0)
    # struct_time 元组共有9 个元素，如果想自定义构造时间元组，则可以通过传入一个数组来实现，如下：
    t3_1 = time.struct_time([2020, 10, 17, 2, 2, 34, 4, 89, 0])  # time.struct_time(tm_year=2020, tm_mon=10, tm_mday=17, tm_hour=2, tm_min=2, tm_sec=34, tm_wday=4, tm_yday=89, tm_isdst=0)

    t4 = time.mktime(t3)  # struct_time 转换为 timestamp


def create_timedelta():
    """
    timedelta 对象表示两个date 或者time 或者datetime 的时间间隔。
    class datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)
    所有参数都是可选的并且默认为0。这些参数可以是整数或者浮点数，也可以是正数或者负数。
    只有days, seconds 和microseconds 会存储在内部。参数单位的换算规则如下：
    • 1 毫秒会转换成1000 微秒。
    • 1 分钟会转换成60 秒。
    • 1 小时会转换成3600 秒。
    • 1 星期会转换成7 天。

    :return:
    """
    delta_ = dt.timedelta(weeks=3, days=10, hours=6, minutes=50, seconds=30, microseconds=1000, milliseconds=10000)

    datetime1_ = dt.datetime(2020, 10, 14, 15, 12, 20, 303269)
    datetime2_ = dt.datetime.now()
    delta_ = datetime1_ - datetime2_

    return delta_


def creat_tzinfo():
    """
    datetime.tzinfo 返回datetime 对象的时区，前提是在创建datetime 对象时需传入tzinfo 参数，如果没有传入则返回值为None 。
    :return:
    """
    import pytz
    sh = pytz.timezone('Asia/Shanghai')
    d_tz = dt.datetime(2020, 10, 12, 8, tzinfo=sh)

    return d_tz.tzinfo


def datetime_format():
    """
    需要注意的是，strftime 方法可供date、time 和datetime 对象使用，而strptime 方法仅供datetime对象使用。
    :return:
    """

    ## 时间对象转字符串  时间对象转换为字符串，可以通过isoformat 或strftime 方法来实现
    d1 = dt.date(2020, 10, 12)
    d11 = d1.isoformat()  # 方法1
    d12 = d1.strftime('%Y-%m-%d')  # 方法2

    t1 = dt.time(20, 10, 1)
    t1.strftime('%H:%M:%S')

    dt2 = dt.datetime.now()
    dt2.strftime('%Y-%m-%d %H:%M:%S')
    dt2.isoformat()

    ## 字符串转时间对象
    s1 = '2020-10-09'
    d = dt.datetime.strptime(s1, '%Y-%m-%d')

    ## 时间戳转换为时间对象
    # 时间戳是指格林威治时间1970 年01 月01 日00 时00 分00 秒(北京时间1970 年01 月01 日08 时00 分00 秒) 起至现在的总秒数。
    ts_1 = time.time()
    ts_2 = int(time.time() / 86400) * 86400  # 获取当天00:00:00 的时间戳
    ts_3 = int(time.time() / 86400) * 86400 + 86400 - 1  # 获取当天23:59:59 的时间戳, 一天有24*60*60 = 86400 秒

    dt.datetime.fromtimestamp(ts_1)
    dt.date.fromtimestamp(ts_1)

    ## 将时间对象转换为时间戳
    dt1 = dt.datetime(2020, 10, 11, 20, 10, 1)
    dt_s = dt.datetime(1970, 1, 1, 8)  # 鉴于，时间戳是指格林威治时间1970 年01 月01 日00 时00 分00 秒(北京时间1970 年01 月01 日08 时00分00 秒) 起至现在的总秒数
    timedelta_1 = dt1 - dt_s  # 返回时间间隔包含了多少秒
    timedelta_s = timedelta_1.total_seconds()


def get_specific_datetime():
    # 获取当天最小时间
    dt.datetime.combine(dt.date.today(), dt.time.min)
    # 获取当天最大时间
    dt.datetime.combine(dt.date.today(), dt.time.max)
    # 获取明天
    dt.date.today() + dt.timedelta(days=1)
    # 获取昨天
    dt.date.today() - dt.timedelta(days=1)
    # 获取本周或本月第一天及最后一天
    d_today = dt.date.today()
    d_today - dt.timedelta(d_today.weekday())  # 获取本周第一天
    d_today + dt.timedelta(6 - d_today.weekday())  # 获取本周最后一天
    # 计算两个日期相差多少天
    dt1 = dt.datetime(2020, 10, 14, 15, 12, 20, 303269)
    dt2 = dt.datetime.now()
    td1 = dt2 - dt1
    td1 = dt.timedelta(days=2, seconds=68539, microseconds=403113)
    td1.days
    td1.seconds
    td1.total_seconds()

    # 时间元组转换为时间戳
    t2 = time.localtime()
    t4 = time.mktime(t2)  # 将一个struct_time 转化为时间戳
    # 时间元组为字符串 time.asctime() 转换由gmtime() 或localtime() 所返回的表示时间的元组或struct_time 为以下形式的字符串: “Sun Jun 20 23:21:05 1993”。
    t5 = time.asctime(t2)  # 'Thu Oct 15 10:26:38 2020'
    t5_1 = time.strftime('%Y-%m-%d', t2)  # 如果想自定义字符串的格式，则可以通过strftime() 来实现。strftime() 可以将时间元组转换为由format参数指定的字符串。
    # 时间戳转换为字符串
    t4 = time.mktime(t2)
    t6 = time.ctime(t4)  # 'Thu Oct 15 10:26:38 2020'
    # 字符串转换为时间元组, strptime() 方式可以将特定格式的字符串解析为时间元组。字符串的默认匹配格式为”%a %b %d %H:%M:%S %Y” 。
    time.strptime('Thu Oct 15 10:30:18 2020')  # time.struct_time(tm_year=2020, tm_mon=10, tm_mday=15, tm_hour=10, tm_min=30, tm_sec=18, tm_wday=3, tm_yday=289, tm_isdst=-1)


def calendar_cal():
    calendar.prcal(2020)  # 获取某年的日历并打印出来
    calendar.month(2020, 10)
    print(calendar.month(2020, 10))  # 使用行数print() 显示更好看
    calendar.prmonth(2020, 10)

    calendar.monthcalendar()  # 返回表示一个月的日历的矩阵。每一行代表一周；此月份外的日子由零表示。每周从周一开始，除非使用setfirstweekday() 改变设置。
    print(calendar.monthcalendar(2020, 10))  # [[0, 0, 0, 1, 2, 3, 4], [5, 6, 7, 8, 9, 10, 11], [12, 13, 14, 15, 16, 17, 18], [19, 20, 21, 22, 23, 24, 25], [26, 27, 28, 29, 30, 31, 0]]
    calendar.weekday()  # 返回某天是星期几，默认情况下0-6 代表周一到周日。
    print(calendar.weekday(2020, 10, 15))  # 获取日期是星期几(0-6 代表周一到周日)
    calendar.setfirstweekday()  # 设置每一周的开始(0 表示星期一，6 表示星期天)。
    calendar.setfirstweekday(calendar.SUNDAY)  # 设置每周的第一天为星期天
    calendar.weekheader()  # 返回一个包含星期几的缩写名的头。n 指定星期几缩写的字符宽度。
    print(calendar.weekheader(3))  # Mon Tue Wed Thu Fri Sat Sun


def get_dates_to_update(start, end):
    start = dt.datetime.strptime(start, '%Y-%m-%d') + dt.timedelta(days=1)
    end = dt.datetime.strptime(end, '%Y-%m-%d')
    dates = []
    while start < end:
        dates.append(start.strftime('%Y-%m-%d'))
        start += dt.timedelta(days=1)
    return dates


# main
if __name__ == '__main__':
    print(get_today(fmt=common_var.PY_DATE_FMT_WITH_N_DASH))
    datetime_format()
